package com.example.support.wechat.general;

import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.codec.digest.DigestUtils;

import com.alibaba.fastjson.JSON;
import com.example.util.HttpUtils;

public class WechatGeneralSdkClient {

	protected final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(getClass());

	public WechatGeneralSdkClient() {
	}

	// 获取Access token
	// https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
	private static final String APP_ACCESSTOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
	private WechatGeneralAppAccesstokenResponse fetchAppAccesstoken0(String appId, String appSecret) throws WechatGeneralException {
		String api = APP_ACCESSTOKEN_URL.replace("APPID", appId).replace("APPSECRET", appSecret);
		return callApi(api, WechatGeneralAppAccesstokenResponse.class);
	}

	// 获取ticket
	// https://developers.weixin.qq.com/doc/offiaccount/WeChat_Invoice/E_Invoice/Vendor_API_List.html#1
	private static final String JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
	private WechatGeneralJsapiTicketResponse fetchJsapiTicket0(String app_access_token) throws WechatGeneralException {
		String api = JSAPI_TICKET_URL.replace("ACCESS_TOKEN", app_access_token);
		return callApi(api, WechatGeneralJsapiTicketResponse.class);
	}

	private static final ConcurrentHashMap<String, WechatGeneralAppAccesstokenResponseWrap> APP_ACCESSTOKEN_MAP = new ConcurrentHashMap<>();
	public String fetchAppAccesstoken(String appId, String appSecret) throws WechatGeneralException {
		WechatGeneralAppAccesstokenResponseWrap response = APP_ACCESSTOKEN_MAP.get(appId);
		if (response == null || response.isExpired()) {
			response = new WechatGeneralAppAccesstokenResponseWrap(fetchAppAccesstoken0(appId, appSecret));
			APP_ACCESSTOKEN_MAP.put(appId, response);
		}
		return response.access_token;
	}

	private static final ConcurrentHashMap<String, WechatGeneralJsapiTicketResponseWrap> JSAPI_TICKET_MAP = new ConcurrentHashMap<>();
	public String fetchJsapiTicket(String appId, String app_access_token) throws WechatGeneralException {
		WechatGeneralJsapiTicketResponseWrap response = JSAPI_TICKET_MAP.get(appId);
		if (response == null || response.isExpired()) {
			response = new WechatGeneralJsapiTicketResponseWrap(fetchJsapiTicket0(app_access_token));
			JSAPI_TICKET_MAP.put(appId, response);
		}
		return response.ticket;
	}

	// JS-SDK使用权限签名算法
	// https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62
	public WechatGeneralJsSdkConfigResponse jsSdkConfig(String appId, String appSecret, String url) {
		String app_access_token = fetchAppAccesstoken(appId, appSecret);
		String jsapi_ticket = fetchJsapiTicket(appId, app_access_token);

		String nonceStr = UUID.randomUUID().toString();
		Long timestamp = System.currentTimeMillis() / 1000;
		String signature = DigestUtils.sha1Hex("jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + url);

		return new WechatGeneralJsSdkConfigResponse(appId, timestamp, nonceStr, signature);
	}

	private static class WechatGeneralAppAccesstokenResponseWrap extends WechatGeneralAppAccesstokenResponse {
		public long expiresAt;
		public WechatGeneralAppAccesstokenResponseWrap(WechatGeneralAppAccesstokenResponse response) {
			super.access_token = response.access_token;
			super.expires_in = response.expires_in;
			this.expiresAt = System.currentTimeMillis() + response.expires_in * 1000 - 600_000;
		}
		public boolean isExpired() {
			return expiresAt < System.currentTimeMillis();
		}
	}

	private static class WechatGeneralJsapiTicketResponseWrap extends WechatGeneralJsapiTicketResponse {
		public long expiresAt;
		public WechatGeneralJsapiTicketResponseWrap(WechatGeneralJsapiTicketResponse response) {
			super.ticket = response.ticket;
			super.expires_in = response.expires_in;
			this.expiresAt = System.currentTimeMillis() + response.expires_in * 1000 - 600_000;
		}
		public boolean isExpired() {
			return expiresAt < System.currentTimeMillis();
		}
	}

	protected <T extends WechatResponse> T callApi(String api, Class<T> resultType) throws WechatException {
		String json;
		try {
			json = HttpUtils.doGet(api);
		} catch (Exception e) {
			String msg = String.format("请求失败, api=%s", api);
			log.error(msg);
			throw new WechatException(msg, e);
		}

		T response;
		try {
			response = JSON.parseObject(json, resultType);
			response.responseBody = json;
		} catch (Exception e) {
			String msg = String.format("解析失败, api=%s ==> %s", api, json);
			log.error(msg);
			throw new WechatException(msg, e);
		}

		if (!response.ok()) {
			String msg = String.format("接口状态错误, api=%s ==> %s", api, json);
			log.error(msg);
			throw new WechatException(msg);
		}

		return response;
	}

}
